AtomicCounters & IndirectBufferCommands

GLSL
OpenGL
Author

Randall Rauwendaal

Published

July 3, 2013

I’ve made use of Atomic Counters and Indirect Buffers in the past, but always in the most straightforward manner. I.e. create a dedicated buffer for the atomic counter, and another for the Indirect Command Buffer, increment the counter in a shader then write the Atomic Counter value into the Indirect Command Buffer using the Image API, ending up with a shader that looks something like below.

#version 420

layout(location = 0) in ivec3 inputBuffer;

layout(r32ui, binding = 0) uniform uimageBuffer outputBuffer;
layout(r32ui, binding = 1) uniform uimageBuffer indirectArrayCommand;
layout(       binding = 0) uniform atomic_uint  atomicCounter;

void main()
{
  // ...
  // do some stuff
  // ...

  if(someCondition == true)
  {
    //increment counter
    int index = int(atomicCounterIncrement(atomicCounter));

    //store stuff in output buffer
    imageStore(outputBuffer, index, uvec4(someStuff)));
  }

  memoryBarrier();

  //Store the atomicCounter value to the count (the first element) of the DrawArraysIndirect command
  imageStore(indirectArrayCommand, 0, uvec4(atomicCounter(atomicCounter)));
}

This works fine, but one annoying thing about this approach is that it consumes an extra image unit (of the max 8 available). Fortunately, it turns out that it is unnecessary to create an extra atomic counter and perform the synchronization with the indirect draw command. It is possible to simply bind the appropriate element of the indirect draw buffer directly to the atomic counter.

// This binds the count element of the Indirect Array Command Buffer directly as an atomic counter in the shader
// (no need for copy from dedicated atomic counter)
glBindBufferRange(GL_ATOMIC_COUNTER_BUFFER,        // Target buffer is the atomic counter
                  0,                               // Binding point, must match the shader
                  IndirectArrayCommandBuffer_id,   // Source buffer is the Indirect Draw Command Buffer
                  0,                               // Offset, 0 for count, 1 for primCount (instances), etc...
                  sizeof(GLuint));

This allows us to get rid of Indirect Buffers image unit binding, which simplifies the shader as shown below. The main reason I’ve found to do this is reduce the number of image units required by the shader, as its very easy to hit the limit of 8.

#version 420

layout(location = 0) in ivec3 inputBuffer;

layout(r32ui, binding = 0) uniform uimageBuffer outputBuffer;
layout(       binding = 0) uniform atomic_uint  atomicCounter;

void main()
{
  // ...
  // do some stuff
  // ...

  if(someCondition == true)
  {
    //increment counter
    int index = int(atomicCounterIncrement(atomicCounter));

    //store stuff in output buffer
    imageStore(outputBuffer, index, uvec4(someStuff)));
  }
}

Citation

BibTeX citation:
@online{rauwendaal2013,
  author = {Randall Rauwendaal},
  title = {AtomicCounters \& {IndirectBufferCommands}},
  date = {2013-07-03},
  url = {https://raegnar.github.io/rauwendaal.net//posts/2013_07_03 - AtomicCounters & IndirectBufferCommands},
  langid = {en}
}
For attribution, please cite this work as:
Randall Rauwendaal. 2013. “AtomicCounters & IndirectBufferCommands.” July 3, 2013. https://raegnar.github.io/rauwendaal.net//posts/2013_07_03 - AtomicCounters & IndirectBufferCommands.